﻿// Copyright 2009 Mike Geise
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

namespace Falcon.Core
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Reflection;
	using System.Web;
	using System.Web.Mvc;
	using System.Web.Routing;
	using Castle.Windsor;
	using Falcon.Core.Controllers;
	using Falcon.Core.Domain;
	using Falcon.Core.Domain.Services;
	using Falcon.Core.Extensions;
	using Falcon.Core.Helpers;
	using Spark;
	using Spark.Web.Mvc;

	public class Application
	{
		// private constants
		private const string SessionKey = "user.session";

		/// <summary>
		/// Gets or sets the packages.
		/// </summary>
		/// <value>The packages.</value>
		private IList<IPackage> Packages
		{
			get; 
			set;
		}

		/// <summary>
		/// Gets or sets the container.
		/// </summary>
		/// <value>The container.</value>
		public static IWindsorContainer Container
		{
			get;
			set;
		}

		/// <summary>
		/// Gets a value indicating whether this instance is web request.
		/// </summary>
		/// <value>
		/// 	<c>true</c> if this instance is web request; otherwise, <c>false</c>.
		/// </value>
		public static bool IsWebRequest
		{
			get
			{
				return HttpContext.Current != null;
			}
		}

		/// <summary>
		/// Registers the packages.
		/// </summary>
		public virtual void RegisterPackages()
		{
			this.Packages = new List<IPackage>();

			var packages = new XmlHelper<Packages>(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "packages.config")).Load();

			if (packages.Items == null)
			{
				return;
			}

			foreach (var package in packages.Items)
			{
				if (!package.Active)
				{
					continue;
				}

				var assembly = Assembly.Load(package.Path);
				var instance = assembly.CreateInstance(package.Type).As<IPackage>();

				if (instance != null)
				{
					this.Packages.Add(instance);
				}
			}
		}

		/// <summary>
		/// Registers the container.
		/// </summary>
		public virtual void RegisterContainer()
		{
			Container = new WindsorContainer(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "components.config"));
		}

		/// <summary>
		/// Registers the controller factory.
		/// </summary>
		public virtual void RegisterControllerFactory()
		{
			ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
		}

		/// <summary>
		/// Registers the view engine.
		/// </summary>
		/// <param name="engines">The engines.</param>
		public virtual void RegisterViewEngine(ICollection<IViewEngine> engines)
		{
			// view engine settings
			var settings = new SparkSettings()
				.SetPageBaseType(typeof(SparkViewPage))
				.SetDebug(false)
				.SetAutomaticEncoding(false)
				.AddAssembly("Falcon.Core")
				.AddNamespace("System")
				.AddNamespace("System.Collections.Generic")
				.AddNamespace("System.Linq")
				.AddNamespace("System.Web.Mvc")
				.AddNamespace("System.Web.Mvc.Ajax")
				.AddNamespace("System.Web.Mvc.Html")
				.AddNamespace("System.Web.Routing")
				.AddNamespace("Falcon.Core.Collections")
				.AddNamespace("Falcon.Core.Domain")
				.AddNamespace("Falcon.Core.Domain.Enums")
				.AddNamespace("Falcon.Core.Domain.Extensions")
				.AddNamespace("Falcon.Core.Extensions");

			// create view engine
			var services = SparkEngineStarter.CreateContainer(settings);

			// register view engine
			SparkEngineStarter.RegisterViewEngine(engines, services);

			// get view factory
			var factory = services.GetService<IViewEngine>().As<SparkViewFactory>();

			// compile views
			CompileViews(factory);
		}

		/// <summary>
		/// Registers the routes.
		/// </summary>
		/// <param name="routes">The routes.</param>
		public virtual void RegisterRoutes(RouteCollection routes)
		{
			// routes to ignore
			routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

			foreach (var package in this.Packages)
			{
				package.RegisterRoutes(routes);
			}

			// core routes
			Routes.Register(routes);

			// default route
			routes.Add(new Route("{controller}/{action}",
				new RouteValueDictionary(new { controller = "home", action = "index" }),
				new MvcRouteHandler()));
		}

		/// <summary>
		/// Compiles the views.
		/// </summary>
		/// <param name="factory">The factory.</param>
		public virtual void CompileViews(SparkViewFactory factory)
		{
			var batch = new SparkBatchDescriptor();

			batch
				.For<AttachmentController>()
				.For<AttachmentRuleController>()
				.For<SystemCountryController>()
				.For<HomeController>()
				.For<SystemLanguageController>()
				.For<PollController>()
				.For<SystemResourceController>()
				.For<SystemSettingController>()
				.For<SystemTimeZoneController>()
				.For<UserAlbumController>()
				.For<UserAlbumPhotoController>()
				.For<UserAuthController>()
				.For<UserAvatarController>()
				.For<SystemUserBadgeController>()
				.For<UserContactController>()
				.For<UserMessageController>()
				.For<UserNoteController>()
				.For<UserProfileController>()
				.For<SystemUserRoleController>();

			foreach (var package in this.Packages)
			{
				package.CompileViews(batch);
			}

			factory.Precompile(batch);
		}

		/// <summary>
		/// Initializes this instance.
		/// </summary>
		public void Initialize()
		{
			// load settings
			Resolve<ISettingService>().Init();

			// load resources
			Resolve<IResourceService>().Init();
		}

		/// <summary>
		/// Resolves this instance.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public static T Resolve<T>()
		{
			return Container.Resolve<T>();
		}

		/// <summary>
		/// Resolves the instance with the specified key.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="key">The key.</param>
		/// <returns></returns>
		public static T Resolve<T>(string key)
		{
			return Container.Resolve<T>(key);
		}

		/// <summary>
		/// Resolves the instance with the specified parameters.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="parameters">The parameters.</param>
		/// <returns></returns>
		public static T Resolve<T>(object parameters)
		{
			return Container.Resolve<T>(parameters);
		}

		/// <summary>
		/// Resolves the instance with the specified type.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="type">The type.</param>
		/// <returns></returns>
		public static T Resolve<T>(Type type)
		{
			return Container.Resolve(type).As<T>();
		}

		/// <summary>
		/// Releases the specified instance.
		/// </summary>
		/// <param name="instance">The instance.</param>
		public static void Release(object instance)
		{
			Container.Release(instance);
		}

		/// <summary>
		/// Populates the user session.
		/// </summary>
		public static void Session()
		{
			Session(false);
		}

		/// <summary>
		/// Populates the user session.
		/// </summary>
		/// <param name="refresh">if set to <c>true</c> [refresh].</param>
		public static void Session(bool refresh)
		{
			var context = HttpContext.Current;
			var identity = User.Identity;

			if (identity == null)
			{
				return;
			}

			identity.Session = context.Session[SessionKey].As<UserSession>();

			if (!refresh && identity.Session != null)
			{
				return;
			}

			identity.Session = Resolve<IUserService>().GetSession(identity.Name);

			context.Session[SessionKey] = identity.Session;
		}

		/// <summary>
		/// Destroys and abandons the current user session
		/// </summary>
		public static void SessionDestroy()
		{
			var context = HttpContext.Current;

			context.Session[SessionKey] = null;
			context.Session.Abandon();
		}
	}
}